// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_error.h"
#include "mk_scene_manager.h"
#include "mk_scene.h"

namespace MK3DE
{
	CSceneManager::CSceneManager()
	{

	}

	CSceneManager::~CSceneManager()
	{
		std::vector<CScene*>::iterator i = _vecScene.begin();
		while (i != _vecScene.end())
		{
			CScene* scene = *i;
			SAFE_RELEASE(scene);
			++i;
		}
	}

	CScene* CSceneManager::CreateScene(const char* sceneName)
	{
		CScene* scene = new CScene;
		MK_PROCESS_ERROR(scene);

		scene->SetName(sceneName);
		_vecScene.push_back(scene);

Exit0:
		return scene;
	}

	CScene* CSceneManager::LoadSceneFromFile(const char* fileName)
	{
		return NULL;
	}

	BOOL CSceneManager::RemoveScene(const char* sceneName)
	{
		std::vector<CScene*>::iterator i;
		if (__GetSceneByName(sceneName, i))
		{
			_vecScene.erase(i);
			return TRUE;
		}

		return FALSE;
	}

	CScene* CSceneManager::GetScene(const char* sceneName)
	{
		std::vector<CScene*>::iterator i;
		if (__GetSceneByName(sceneName, i))
			return *i;

		return NULL;
	}

	BOOL CSceneManager::__GetSceneByName(const char* sceneName, std::vector<CScene*>::iterator& i)
	{
		i = _vecScene.begin();
		while (i != _vecScene.end())
		{
			CScene* scene = *i;
			if (scene->GetName().compare(sceneName) == 0)
				return TRUE;

			++i;
		}

		return FALSE;
	}

	void CSceneManager::Update(DWORD dt)
	{
		std::vector<CScene*>::iterator i = _vecScene.begin();
		while (i != _vecScene.end())
		{
			CScene* scene = *i;
			scene->Update(dt, FALSE);

			++i;
		}
	}

	//	CSceneManager::CSceneManager() : 
	//		CSceneNode(NULL)
	//	{
	//        _skyBox = NULL;
	//        _renderStage = RS_NORMAL;
	//        _texReflection = NULL;
	//        _texRefraction = NULL;
	//	}
	//
	//	CSceneManager::~CSceneManager()
	//	{
	//        SAFE_RELEASE(_texReflection);
	//        SAFE_RELEASE(_texRefraction);
	//	}
	//
	//	CSceneNode* CSceneManager::CreateSceneNode(CSceneNode* parent, SCENE_NODE_TYPE type)
	//	{
	//		CSceneNode* node = NULL;
	//		switch (type)
	//		{
	//        case SNT_CAMERA:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add camera scene node.");
	//                node = new CCamera(parent);
	//            }
	//            break;
	//
	//		case SNT_MODEL:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add model scene node.");
	//			    node = new CRepresentModel(parent);
	//            }
	//			break;
	//
	//		case SNT_TERRAIN:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add terrain scene node.");
	//			    node = new CEntityTerrain(parent);
	//            }
	//			break;
	//
	//		case SNT_LIGHT_DIRECTION:
	//			{
	//				GetEngine()->GetLog()->Write(MKLT_INFO, "Add directional light scene node.");
	//				node = new CLightDirection(parent);
	//			}
	//			break;
	//
	//        case SNT_TERRAIN_HEIGHT_BRUSH:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add terrain height brush scene node.");
	//                node = new CTerrainHeightBrush(parent);
	//            }
	//            break;
	//
	//        case SNT_TERRAIN_LAYER_BRUSH:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add terrain layer brush scene node.");
	//                node = new CTerrainLayerBrush(parent);
	//            }
	//            break;
	//
	//        case SNT_SKY_BOX:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add sky box scene node.");
	//                node = new CEntitySkyBox(parent);
	//
	//                if (_skyBox)
	//                    DestroySceneNode(_skyBox);
	//                _skyBox = (CEntitySkyBox*)node;
	//            }
	//            break;
	//
	//        case SNT_PARTICLE_SYSTEM:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add particle system scene node.");
	//                node = new CRepresentParticleSystem(parent);
	//            }
	//            break;
	//
	//        case SNT_WATER_REGION:
	//            {
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Add water region scene node.");
	//                node = new CRepresentWaterRegion(parent);
	//            }
	//            break;
	//
	//		default:
	//			break;
	//		}
	//
	//		if (!node)
	//			return NULL;
	//
	//        if (parent)
	//		    parent->_vecChild.push_back(node);
	//
	//		return node;
	//	}
	//
	//    BOOL CSceneManager::DestroySceneNode(CSceneNode* node)
	//    {
	//        CSceneNode* parent = node->GetParent();
	//        if (parent)
	//            parent->RemoveChild(node);
	//        SAFE_DELETE(node);
	//        return TRUE;
	//    }
	//
	//    BOOL CSceneManager::AddSceneNode(CSceneNode* node)
	//    {
	//        CSceneNode* parent = node->GetParent();
	//        if (!parent)
	//            parent = this;
	//        else
	//            parent->RemoveChild(node);
	//        parent->_vecChild.push_back(node);
	//        node->SetParent(parent);
	//        return TRUE;
	//    }
	//
	//    BOOL CSceneManager::LoadFromFile(const char* fileName)
	//    {
	//        BOOL ret = FALSE;
	//        int result = 1;
	//        TiXmlDocument* pXML = NULL;
	//        TiXmlElement* node = NULL;
	//        int regionWidth = 0;
	//        int regionHeight = 0;
	//        
	//        CMemoryBlock block;
	//        ret = GetEngine()->GetFileSystem()->ReadFile(fileName, &block);
	//        MK_PROCESS_ERROR(ret);
	//
	//        pXML = new TiXmlDocument();
	//        MK_PROCESS_ERROR(pXML);
	//
	//        pXML->SetCondenseWhiteSpace(false);
	//        pXML->Parse((const char*)block.GetBuffer());
	//        MK_PROCESS_ERROR(pXML->Error() == false);
	//
	//        node = pXML->RootElement();
	//        MK_PROCESS_ERROR(node);
	//
	//        result = strcmp(node->Value(), "GameWorld");
	//        MK_PROCESS_ERROR(result == 0);
	//
	//        node = node->FirstChildElement("SceneGraph");
	//        MK_PROCESS_ERROR(node);
	//
	//        node = node->FirstChildElement();
	//        while (node)
	//        {
	//            if (strcmp(node->Value(), "Terrain") == 0)
	//            {
	//                CEntityTerrain* terrain = NULL;
	//                const char* terrainFile = node->Attribute("FileName");
	//                MK_PROCESS_ERROR(terrainFile);
	//
	//                terrain = (CEntityTerrain*)CreateSceneNode(this, SNT_TERRAIN);
	//                MK_PROCESS_ERROR(terrain);
	//
	//                ret = terrain->LoadFromFile(terrainFile);
	//                MK_PROCESS_ERROR(ret);
	//            }
	//            else if(strcmp(node->Value(), "Model") == 0)
	//            {
	//                TiXmlElement* transformNode = NULL;
	//                CRepresentModel* model = NULL;
	//                float x, y, z, w;
	//                const char* modelFile = node->Attribute("FileName");
	//                MK_PROCESS_ERROR(modelFile);
	//
	//                model = (CRepresentModel*)CreateSceneNode(this, SNT_MODEL);
	//                MK_PROCESS_ERROR(model);
	//
	//                ret = model->LoadFromFile(modelFile);
	//                MK_PROCESS_ERROR(ret);
	//
	//                transformNode = node->FirstChildElement("Transform");
	//                MK_PROCESS_ERROR(transformNode);
	//
	//                {
	//                    TiXmlElement* translationNode = transformNode->FirstChildElement("Translation");
	//                    MK_PROCESS_ERROR(translationNode);
	//                    
	//                    ret = translationNode->QueryFloatAttribute("x", &x);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    ret = translationNode->QueryFloatAttribute("y", &y);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    ret = translationNode->QueryFloatAttribute("z", &z);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    model->SetRelativeTranslation(D3DXVECTOR3(x, y, z));
	//                }
	//
	//                {
	//                    TiXmlElement* rotationNode = transformNode->FirstChildElement("Rotation");
	//                    MK_PROCESS_ERROR(rotationNode);
	//
	//                    ret = rotationNode->QueryFloatAttribute("x", &x);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    ret = rotationNode->QueryFloatAttribute("y", &y);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    ret = rotationNode->QueryFloatAttribute("z", &z);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    ret = rotationNode->QueryFloatAttribute("w", &w);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    model->SetRelativeRotation(D3DXQUATERNION(x, y, z, w));
	//                }
	//
	//
	//                {
	//                    TiXmlElement* scaleNode = transformNode->FirstChildElement("Scale");
	//                    MK_PROCESS_ERROR(scaleNode);
	//
	//                    ret = scaleNode->QueryFloatAttribute("x", &x);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    ret = scaleNode->QueryFloatAttribute("y", &y);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    ret = scaleNode->QueryFloatAttribute("z", &z);
	//                    MK_PROCESS_ERROR(ret == TIXML_SUCCESS);
	//
	//                    model->SetRelativeScale(D3DXVECTOR3(x, y, z));
	//                }
	//            }
	//
	//            node = node->NextSiblingElement();
	//        }
	//
	//        ret = TRUE;
	//Exit0:
	//        SAFE_DELETE(pXML);
	//
	//        return ret;
	//    }
	//
	//    void CSceneManager::_AddToRenderQueue(CSceneNode* node)
	//    {
	//        RENDER_QUEUE_TYPE rqt = node->GetRenderQueueType();
	//        ASSERT(rqt < RQT_MAX);    
	//        if (_renderStage == RS_NORMAL)
	//            _listRenderQueue[rqt].push_back(node);
	//        else
	//        {
	//            _listRenderQueueReflection[rqt].push_back(node);
	//
	//        }
	//    }
	//
	//    BOOL CSceneManager::PreRender()
	//    {
	//        std::vector<CSceneNode*>::iterator i = _vecChild.begin();
	//        while (i != _vecChild.end())
	//        {
	//            CSceneNode* node = *i;
	//            if (!node->PreRender())
	//                GetEngine()->GetLog()->Write(MKLT_INFO, "Post render scene node[type: %d, id: %d] failed.", node->GetType(), node->GetId());
	//            ++i;
	//        }
	//
	//        return TRUE;        
	//    }
	//
	//    BOOL CSceneManager::Render()
	//    {
	//        if (_renderStage == RS_NORMAL || _renderStage == RS_REFRACTION)
	//        {
	//            for (int i = 0; i < (int)RQT_MAX; ++i)
	//            {
	//                RENDER_QUEUE& rq = _listRenderQueue[i];
	//                RENDER_QUEUE::iterator it = rq.begin();
	//                while (it != rq.end())
	//                {
	//                    CSceneNode*& node = *it;
	//                    node->Render();
	//                    ++it;
	//                }
	//
	//#if MK_PROFILER_ENABLE
	//                GetEngine()->GetProfiler()->AddEntityDrawCount((DWORD)rq.size());
	//#endif
	//
	//                if (_renderStage != RS_REFRACTION)
	//                    rq.clear();
	//            }
	//        }
	//        else if (_renderStage == RS_REFLECTION)
	//        {
	//            for (int i = 0; i < (int)RQT_MAX; ++i)
	//            {
	//                RENDER_QUEUE& rq = _listRenderQueueReflection[i];
	//                RENDER_QUEUE::iterator it = rq.begin();
	//                while (it != rq.end())
	//                {
	//                    CSceneNode*& node = *it;
	//                    node->Render();
	//                    ++it;
	//                }
	//
	//#if MK_PROFILER_ENABLE
	//                GetEngine()->GetProfiler()->AddEntityDrawCount((DWORD)rq.size());
	//#endif
	//
	//                rq.clear();
	//            }
	//
	//        }
	//
	//        return TRUE;
	//    }
	//
	//    BOOL CSceneManager::RenderAll()
	//    {
	//        CEngine* pEngine = GetEngine();
	//        CRenderDevice* renderDevice = pEngine->GetRenderDevice();
	//        CCamera* pCamera = pEngine->GetCamera();
	//
	//        // find out all scene node in frustum.
	//        _renderStage = RS_NORMAL;
	//        PreRender();
	//
	//        // check whether there are any water region.
	//        if (!_listRenderQueue[RQT_WATER].empty())
	//        {
	//            // calculate average water surface height.
	//            float height = 0;
	//            for (RENDER_QUEUE::iterator it = _listRenderQueue[RQT_WATER].begin(); it != _listRenderQueue[RQT_WATER].end(); ++it)
	//                height += (*it)->GetRelativeTranslation().y;
	//            
	//            height /= (float)_listRenderQueue[RQT_WATER].size();
	//
	//            // make a transformation matrix of reflection.
	//            D3DXPLANE planeWater, planeWaterInv;
	//            D3DXMATRIX matReflection;
	//            D3DXPlaneFromPointNormal(&planeWater, &D3DXVECTOR3(0, height, 0), &D3DXVECTOR3(0, 1, 0));
	//            D3DXPlaneFromPointNormal(&planeWaterInv, &D3DXVECTOR3(0, height, 0), &D3DXVECTOR3(0, -1, 0));
	//            D3DXMatrixReflect(&matReflection, &planeWater);
	//            
	//            // make a reflected camera.
	//            D3DXVECTOR3 posSave = pCamera->GetPosition();
	//            D3DXVECTOR3 lookDirSave = pCamera->GetLookDirection();
	//            D3DXVECTOR3 upDirSave = pCamera->GetUpDirection();
	//
	//            D3DXVECTOR3 pos, lookDir, upDir;
	//            D3DXVec3TransformCoord(&pos, &posSave, &matReflection);
	//            D3DXVec3TransformNormal(&lookDir, &lookDirSave, &matReflection);
	//            D3DXVec3TransformNormal(&upDir, &upDirSave, &matReflection);
	//
	//            pCamera->SetPosition(pos);
	//            pCamera->SetLookDirection(lookDir);
	//            pCamera->SetUpDirection(upDir);
	//            pCamera->AddCustomPlane(planeWater);
	//            pCamera->Update(0);
	//
	//            // find out all reflection scene node.
	//            _renderStage = RS_REFLECTION;
	//            PreRender();
	//
	//            // render reflection texture.
	//            D3DXMATRIX m2;
	//            D3DXMatrixInverse(&m2, NULL, &pCamera->GetViewMatrix());
	//            D3DXMatrixTranspose(&m2, &m2);
	//
	//            D3DXMATRIX m3;
	//            D3DXMatrixInverse(&m3, NULL, &pCamera->GetProjMatrix());
	//            D3DXMatrixTranspose(&m3, &m3);
	//
	//            D3DXMATRIX m = m2 * m3;
	//            D3DXPlaneTransform(&planeWater, &planeWater, &m);
	//
	//            __RenderReflectionTexture(renderDevice, planeWater);
	//
	//            // Restore camera.
	//            pCamera->ClearAllCustomPlane();
	//            pCamera->SetPosition(posSave);
	//            pCamera->SetLookDirection(lookDirSave);
	//            pCamera->SetUpDirection(upDirSave);
	//            pCamera->Update(0);
	//
	//            // render refraction texture.
	//            _renderStage = RS_REFRACTION;
	//
	//            D3DXMatrixInverse(&m2, NULL, &pCamera->GetViewMatrix());
	//            D3DXMatrixTranspose(&m2, &m2);
	//
	//            D3DXMatrixInverse(&m3, NULL, &pCamera->GetProjMatrix());
	//            D3DXMatrixTranspose(&m3, &m3);
	//
	//            m = m2 * m3;
	//            D3DXPlaneTransform(&planeWaterInv, &planeWaterInv, &m);
	//
	//            __RenderRefractionTexture(renderDevice, planeWaterInv);
	//        }
	//
	//        // render entire scene.
	//        _renderStage = RS_NORMAL;
	//
	//        BOOL clearColor = TRUE;
	//        if (_skyBox)
	//        {
	//            clearColor = FALSE;
	//            _skyBox->Render();
	//        }
	//
	//        renderDevice->Clear(clearColor, TRUE, TRUE, pEngine->GetBkColor(), 1.0f, 0);
	//
	//        Render();
	//        PostRender();
	//
	//        //GetEngine()->GetPainter()->DrawImage2D(Rect2D(0, 0, 256, 256), RectColor(), _texReflection);
	//        //GetEngine()->GetPainter()->Flush();
	//        //GetEngine()->GetPainter()->DrawImage2D(Rect2D(256, 0, 512, 256), RectColor(), _texRefraction);
	//        //GetEngine()->GetPainter()->Flush();
	//
	//        return TRUE;
	//    }
	//
	//    BOOL CSceneManager::__RenderReflectionTexture(CRenderDevice* renderDevice, D3DXPLANE& planeWater)
	//    {
	//        BOOL ret = FALSE;
	//        BOOL clearColor = TRUE;
	//        if (!_texReflection)
	//        {
	//            ret = GetEngine()->GetResourceManager()->GetTexture2D(512, 512, PF_A8R8G8B8, 1, TU_RENDER_TARGET, _texReflection);
	//            MK_PROCESS_ERROR(ret);
	//        }
	//
	//        renderDevice->SetRenderTarget(_texReflection);
	//
	//        if (_skyBox)
	//        {
	//            clearColor = FALSE;
	//            _skyBox->Render();
	//        }
	//
	//        renderDevice->Clear(clearColor, TRUE, TRUE, MK_COLOR_ARGB(0, 255, 255, 255), 1.0f, 0);
	//        renderDevice->SetClipPlane(0, planeWater, TRUE);
	//        Render();
	//        renderDevice->SetClipPlane(0, D3DXPLANE(0, 0, 0, 0), FALSE);
	//        renderDevice->SetRenderTarget(NULL);
	//            
	//        ret = TRUE;
	//Exit0:
	//        return ret;
	//    }
	//
	//    BOOL CSceneManager::__RenderRefractionTexture(CRenderDevice* renderDevice, D3DXPLANE& planeWater)
	//    {
	//        BOOL ret = FALSE;
	//        if (!_texRefraction)
	//        {
	//            ret = GetEngine()->GetResourceManager()->GetTexture2D(512, 512, PF_A8R8G8B8, 1, TU_RENDER_TARGET, _texRefraction);
	//            MK_PROCESS_ERROR(ret);
	//        }
	//
	//        renderDevice->SetRenderTarget(_texRefraction);
	//        renderDevice->Clear(TRUE, TRUE, TRUE, MK_COLOR_ARGB(0, 255, 255, 255), 1.0f, 0);
	//        renderDevice->SetClipPlane(0, planeWater, TRUE);
	//        Render();
	//        renderDevice->SetClipPlane(0, D3DXPLANE(0, 0, 0, 0), FALSE);
	//        renderDevice->SetRenderTarget(NULL);
	//
	//        ret = TRUE;
	//Exit0:
	//        return ret;
	//    }
}